﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.Net;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.UI.WebControls;
using System.Xml;


namespace widgets.TwitterFeed {
  public partial class WidgetsTwitterFeed : WidgetBase {
    private const string CacheKey = "twitterfeed";
    private const string Link = "<a href=\"{0}{1}\" rel=\"nofollow\" target=\"_blank\">{1}</a>";
    private const string Link2 = "<a href=\"{0}{1}\" rel=\"nofollow\" target=\"_blank\">{2}</a>";
    private static readonly Regex Regex =
      new Regex( "((http://|https://|www\\.)([A-Z0-9.\\-]{1,})\\.[0-9A-Z?;~&\\(\\)#,=\\-_\\./\\+]{2,})",
                RegexOptions.Compiled | RegexOptions.IgnoreCase );
    private static readonly Regex Regex2 = new Regex( "(@\\w+)", RegexOptions.Compiled | RegexOptions.IgnoreCase );
    private static DateTime LastModified = DateTime.MinValue;
    private Uri RequestUri;

    public WidgetsTwitterFeed() {
      NumberOfTwits = 3;
      ShowReplies = false;
    }

    public string Username { get; set; }

    public string Password { get; set; }

    public int NumberOfTwits { get; set; }

    public bool ShowReplies { get; set; }

    public string UserPage { get; set; }
    
    public string PicturePath { get; set; }

    public bool LinkUsernames { get; set; }

    public override string Name {
      get { return "TwitterFeed"; }
    }

    public override bool IsEditable {
      get { return true; }
    }

    protected void repItems_ItemDataBound(object sender, RepeaterItemEventArgs e) {
      var text = (Label)e.Item.FindControl("lblItem");
      var date = (Label)e.Item.FindControl("lblDate");
      var twit = (Twit)e.Item.DataItem;
      text.Text = twit.Title;
      date.Text = twit.PubDate.ToString("d. MMMM HH:mm");
      if (!string.IsNullOrEmpty(PicturePath)) {
        var pic = (Image)e.Item.FindControl("picTwittericon");
        pic.ImageUrl = PicturePath;
        pic.Visible = true;
      } else {
        var pic = (Image)e.Item.FindControl( "picTwittericon" );
        pic.Visible = false;
      }

    }

    private void BeginGetFeed(string url) {
      var request = (HttpWebRequest)WebRequest.Create(url);
      request.Credentials = new NetworkCredential(Username, Password);
      request.BeginGetResponse(EndGetResponse, request);
    }

    private static void EndGetResponse(IAsyncResult result) {
      var request = (HttpWebRequest)result.AsyncState;
      using (var response = (HttpWebResponse)request.GetResponse()) {
        var doc = new XmlDocument();
        doc.Load(response.GetResponseStream());
        HttpRuntime.Cache[CacheKey] = doc.OuterXml;
      }
    }

    private void BindFeed(XmlNode doc, int maxItems) {
      XmlNodeList items = doc.SelectNodes("//channel/item");
      var twits = new List<Twit>();
      int count = 0;
      if (items != null) {
        for (int i = 0; i < items.Count; i++) {
          if (count == maxItems)
            break;

          XmlNode node = items[i];
          var twit = new Twit();
          string title = node.SelectSingleNode("description").InnerText;

          if (!ShowReplies && title.StartsWith("@")) continue;

          if (title.Contains(":")) {
            int start = title.IndexOf(":") + 1;
            title = title.Substring(start);
          }

          twit.Title = LinkUsernames ? ResolveTweeter(title) : ResolveLinks(title);
          twit.PubDate = DateTime.Parse(node.SelectSingleNode("pubDate").InnerText, CultureInfo.InvariantCulture);
          twits.Add(twit);

          count++;
        }
      }

      twits.Sort();
      this.repItems.DataSource = twits;
      this.repItems.DataBind();
    }

    private static string ResolveLinks( string body ) {
      CultureInfo info = CultureInfo.InvariantCulture;

      foreach ( Match match in Regex.Matches( body ) ) {
        body = !match.Value.Contains( "://" )
                 ? body.Replace( match.Value, string.Format( info, Link, "http://", match.Value ) )
                 : body.Replace( match.Value, string.Format( info, Link, string.Empty, match.Value ) );
      }

      return body;
    }

    private static string ResolveTweeter( string body ) {
      CultureInfo info = CultureInfo.InvariantCulture;

      foreach ( Match match in Regex.Matches( body ) ) {
        body = !match.Value.Contains( "://" )
                 ? body.Replace( match.Value, string.Format( info, Link, "http://", match.Value ) )
                 : body.Replace( match.Value, string.Format( info, Link, string.Empty, match.Value ) );
      }

      foreach ( Match match in Regex2.Matches( body ) ) {
        body = body.Replace( match.Value, string.Format( info, Link2, "http://twitter.com/", match.Value.Replace("@", ""), match.Value ) );
      }

      return body;
    }

    public override void LoadWidget() {
      StringDictionary settings = GetSettings();
      Username = settings["username"];
      if (Username == null) return;
      bool showRep;
      if (!bool.TryParse(settings["showreplies"], out showRep))
        showRep = false;
      ShowReplies = showRep;
      bool linkUsr;
      if ( !bool.TryParse( settings["linkusernames"], out linkUsr ) )
        linkUsr = false;
      LinkUsernames = linkUsr;
      int maxItems;
      if (!int.TryParse(settings["maxitems"], out maxItems))
        maxItems = 3;
      UserPage = settings["userpage"];
      PicturePath = settings["picname"];
      if ( !string.IsNullOrEmpty(settings["picname"]))
        PicturePath = BlogEngine.Core.Utils.RelativeWebRoot + settings["picname"]; 
      else
        PicturePath = null;

      this.RequestUri = new Uri(string.Format(@"http://twitter.com/statuses/user_timeline/{0}.rss", Username));
      if (!string.IsNullOrEmpty(UserPage)) {
        this.hlTwitterAccount.Text = UserPage;
        this.hlTwitterAccount.NavigateUrl = string.Format( @"http://twitter.com/{0}", Username );
      } else
        this.hlTwitterAccount.Visible = false;

      if (HttpRuntime.Cache[CacheKey] != null) {
        var xml = (string)HttpRuntime.Cache[CacheKey];
        var doc = new XmlDocument();
        doc.LoadXml(xml);
        BindFeed(doc, maxItems);
      }

      if (DateTime.Now <= LastModified.AddMinutes(15))
        return;
      LastModified = DateTime.Now;
      BeginGetFeed(this.RequestUri.ToString());
    }

    #region Nested type: Twit
    private struct Twit : IComparable<Twit> {
      public DateTime PubDate;
      public string Title;

      #region IComparable<Twit> Members
      public int CompareTo(Twit other) {
        return other.PubDate.CompareTo(this.PubDate);
      }
      #endregion
    }
    #endregion
  }
}